1 /* 2 * Hunt - a framework for web and console application based on Collie using Dlang development 3 * 4 * Copyright (C) 2015-2017 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: HuntLabs 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.http.response; 13 14 import std.datetime; 15 import std.json; 16 17 import collie.codec.http.headers.httpcommonheaders; 18 import collie.codec.http.server.responsehandler; 19 import collie.codec.http.server.responsebuilder; 20 import collie.codec.http.httpmessage; 21 import kiss.logger; 22 import hunt.http.cookie; 23 import hunt.utils.string; 24 import hunt.versions; 25 26 enum XPoweredBy = "Hunt " ~ HUNT_VERSION; 27 28 final class Response : ResponseBuilder 29 { 30 this(ResponseHandler resp) 31 { 32 super(resp); 33 setHttpStatusCode(200); 34 } 35 36 auto setHeader(T = string)(string key, T value) 37 { 38 header!T(key,value); 39 40 return this; 41 } 42 43 auto setHeader(T = string)(HTTPHeaderCode key, T value) 44 { 45 header!T(key,value); 46 47 return this; 48 } 49 50 auto setContext(string str) 51 { 52 setBody(cast(ubyte[]) str); 53 54 return this; 55 } 56 57 auto setContext(ubyte[] data) 58 { 59 setBody(data); 60 61 return this; 62 } 63 64 ///set http status code eg. 404 200 65 auto setHttpStatusCode(ushort code) 66 { 67 status(code,HTTPMessage.statusText(code)); 68 69 return this; 70 } 71 72 ///return json value 73 auto json(JSONValue js) 74 { 75 json(js.toString()); 76 77 return this; 78 } 79 ///render json string value 80 auto json(string jsonString) 81 { 82 setHeader(HTTPHeaderCode.CONTENT_TYPE, "application/json;charset=utf-8").setContext(jsonString); 83 84 return this; 85 } 86 87 ///render html string 88 auto html(string htmlString, string content_type = "text/html;charset=utf-8") 89 { 90 setHeader(HTTPHeaderCode.CONTENT_TYPE, content_type).setContext(htmlString); 91 92 return this; 93 } 94 95 ///render plain text string 96 auto plain(string textString, string content_type = "text/plain;charset=utf-8") 97 { 98 setHeader(HTTPHeaderCode.CONTENT_TYPE, content_type).setContext(textString); 99 100 return this; 101 } 102 103 ///download file 104 auto download(string filename,ubyte[] file, string content_type = "binary/octet-stream") 105 { 106 import std.conv; 107 setHeader(HTTPHeaderCode.CONTENT_TYPE, content_type) 108 .setHeader(HTTPHeaderCode.CONTENT_DISPOSITION, 109 "attachment; filename="~filename~"; size="~(file.length.to!string)) 110 .setContext(file); 111 112 return this; 113 } 114 115 /** 116 * set Cookie 117 */ 118 auto setCookie(string name, string value, int expires = 0, string path = "/", string domain = null) 119 { 120 121 import std.typecons; 122 auto cookie = scoped!Cookie(name, value, [ 123 "path": path, 124 "domain": domain 125 ]); 126 127 if (expires != 0) 128 { 129 import std.conv; 130 cookie.params["max-age"] = (expires < 0) ? "0" : to!string(expires); 131 } 132 133 setHeader(HTTPHeaderCode.SET_COOKIE, cookie.output("")); 134 135 return this; 136 } 137 138 /** 139 * delete Cookie 140 */ 141 auto delCookie(string name) 142 { 143 setCookie(name,null,0); 144 } 145 146 pragma(inline) final void done() 147 { 148 if(_isDone) return; 149 150 _isDone = true; 151 setHeader(HTTPHeaderCode.X_POWERED_BY, XPoweredBy); 152 sendWithEOM(); 153 } 154 155 void redirect(string url, bool is301 = false) 156 { 157 if(_isDone) return; 158 159 setHttpStatusCode((is301 ? 301 : 302)); 160 setHeader(HTTPHeaderCode.LOCATION, url); 161 162 connectionClose(); 163 done(); 164 } 165 166 void do404(string body_ = "",string contentype = "text/html;charset=UTF-8") 167 { 168 doError(404 , body_ , contentype); 169 } 170 171 void do403(string body_ = "",string contentype = "text/html;charset=UTF-8") 172 { 173 doError(403 , body_ , contentype); 174 } 175 176 void doError(ushort code , string body_ = "",string contentype = "text/html;charset=UTF-8") 177 { 178 if(_isDone) return; 179 180 setHttpStatusCode(code); 181 header(HTTPHeaderCode.CONTENT_TYPE, contentype); 182 setContext(errorPageHtml(code , body_)); 183 connectionClose(); 184 done(); 185 } 186 187 void setHttpError(ushort code) 188 { 189 this.setHttpStatusCode(code); 190 this.setContext(errorPageHtml(code)); 191 } 192 193 package(hunt.http): 194 void clear() 195 { 196 setResponseHandler(null); 197 } 198 private: 199 bool _isDone = false; 200 } 201 202 import hunt.http.code; 203 204 string errorPageHtml(int code , string _body = "") 205 { 206 import std.conv : to; 207 208 string text = code.to!string; 209 text ~= " "; 210 text ~= HTTPCodeText(code); 211 212 string html = `<!doctype html> 213 <html lang="en"> 214 <meta charset="utf-8"> 215 <title>`; 216 217 html ~= text; 218 html ~= `</title> 219 <meta name="viewport" content="width=device-width, initial-scale=1"> 220 <style> 221 222 * { 223 line-height: 3; 224 margin: 0; 225 } 226 227 html { 228 color: #888; 229 display: table; 230 font-family: sans-serif; 231 height: 100%; 232 text-align: center; 233 width: 100%; 234 } 235 236 body { 237 display: table-cell; 238 vertical-align: middle; 239 margin: 2em auto; 240 } 241 242 h1 { 243 color: #555; 244 font-size: 2em; 245 font-weight: 400; 246 } 247 248 p { 249 margin: 0 auto; 250 width: 90%; 251 } 252 253 </style> 254 </head> 255 <body> 256 <h1>`; 257 html ~= text; 258 html ~= `</h1> 259 <p>Sorry!! Unable to complete your request :(</p> 260 `; 261 if(_body.length > 0) 262 html ~= "<p>" ~ _body ~ "</p>"; 263 html ~= ` 264 </body> 265 </html> 266 `; 267 268 return html; 269 }